package com.kangjia.model.m0.dao;

import java.io.Serializable;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Projections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.kangjia.general.dao.query.ConditionBuilder;
import com.kangjia.general.dao.query.Param;
import com.kangjia.general.dao.query.QueryParam;
import com.kangjia.general.dao.query.order.Order;
import com.kangjia.general.dao.query.order.OrderBuilder;

@Repository
public class ReportDao {

	@Autowired
	private SessionFactory sessionFactory;

	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
	public <T> void saveOrUpdate(T entity) {
		Session session = sessionFactory.getCurrentSession();
		session.saveOrUpdate(entity);
	}

	@SuppressWarnings("unchecked")
	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
	public <T> void delete(Class<T> clazz, Serializable id) {
		Session session = sessionFactory.getCurrentSession();
		T entity = (T) session.get(clazz, id);
		session.delete(entity);
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public <T> T find(Class<T> clazz, Serializable pk) {
		Session session = sessionFactory.getCurrentSession();
		return (T) session.get(clazz, pk);
	}
	
	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public <T> T find(Class<T> clazz, QueryParam queryParam) {
		Session session = sessionFactory.getCurrentSession();
		Criteria criteria = session.createCriteria(clazz);
		if (queryParam != null) {
			builderCondition(criteria, queryParam.getParams());
			builderOrder(criteria, queryParam.getOrders());
			criteria.setMaxResults(1);
		}
		return (T) criteria.uniqueResult();
	}

	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public <T> List<T> query(Class<T> clazz, QueryParam queryParam) {
		Session session = sessionFactory.getCurrentSession();
		Criteria criteria = session.createCriteria(clazz);
		if (queryParam != null) {
			builderCondition(criteria, queryParam.getParams());
			builderOrder(criteria, queryParam.getOrders());
			if (queryParam.isPager()) {
				queryParam.setTotal(getTotal(criteria));
				criteria.setProjection(null);
				criteria.setFirstResult(queryParam.getStart());
				criteria.setMaxResults(queryParam.getLimit());
			}
		}
		return criteria.list();
	}
	
	@Transactional(readOnly = true)
	public <T> Integer count(Class<T> clazz, QueryParam queryParam) {
		Session session = sessionFactory.getCurrentSession();
		Criteria criteria = session.createCriteria(clazz);
		if (queryParam != null) {
			builderCondition(criteria, queryParam.getParams());
		}
		return getTotal(criteria);
	}

	protected void builderCondition(Criteria criteria, List<Param<?>> params) {
		ConditionBuilder builder = new ConditionBuilder(criteria);
		for (Param<?> param : params) {
			builder.builder(param);
		}
	}

	protected void builderOrder(Criteria criteria, List<Order> orders) {
		OrderBuilder builder = new OrderBuilder(criteria);
		for (Order order : orders) {
			builder.builder(order);
		}
	}

	protected Integer getTotal(Criteria criteria) {
		criteria.setProjection(Projections.rowCount());
		Object object = criteria.uniqueResult();
		if (object != null) {
			return Integer.parseInt(String.valueOf(object));
		}
		return 0;
	}
	
}
